# frozen_string_literal: true

require 'spec_helper'

RSpec.describe Security::VulnerabilityScanning::CreateVulnerabilityService, feature_category: :software_composition_analysis do
  let_it_be(:user) { create(:user) }
  let_it_be(:pipeline) { create(:ci_pipeline, user: user) }
  let(:sbom_source) { build(:ci_reports_sbom_source) }
  let(:affected_component) { build(:vs_affected_component, purl_type: 'npm') }
  let(:advisories) { build_list(:vs_advisory, 3, affected_component: affected_component) }

  subject(:service_response) do
    described_class.execute(
      project: pipeline.project,
      pipeline: pipeline,
      params: {
        sbom_source: sbom_source,
        advisories: advisories
      }
    )
  end

  describe '#execute' do
    context 'when the component type is supported' do
      it 'creates new vulnerabilities' do
        expect(service_response.success?).to be(true)
        expect(service_response.payload[:error]).to be_nil

        vuln_ids = service_response.payload[:vulnerability_ids]
        expect(vuln_ids).not_to be_empty

        vulns_created = Vulnerability.find(vuln_ids)
        expected_vulns = advisories.map do |advisory|
          have_attributes(
            author_id: user.id,
            project_id: pipeline.project_id,
            state: 'detected',
            confidence: 'unknown',
            report_type: 'dependency_scanning',
            present_on_default_branch: true,
            title: advisory.title,
            severity: advisory.cvss_v3.severity.downcase,
            finding_description: advisory.description,
            solution: advisory.solution
          )
        end
        expect(vulns_created).to match_array(expected_vulns)
      end
    end

    context 'when the component type is not supported' do
      let(:affected_component) { build(:vs_affected_component, purl_type: 'apk') }

      it 'fails to create a new vulnerability with error' do
        expect(service_response.success?).to be(false)
        expect(service_response.payload[:vulnerability_ids]).to be_nil
        expect(service_response.payload[:error]).not_to be_nil
        expect(service_response.payload[:error]).to be_a_kind_of(
          StandardError
        )
        expect(service_response.payload[:error].message).to eq(
          "No finding builder found for component with purl type 'apk'"
        )
      end
    end

    context 'when the pipeline has no associated user' do
      let_it_be(:pipeline) { create(:ci_pipeline) }

      it 'fails to create a new vulnerability with ArgumentError' do
        expect(service_response.success?).to be(false)
        expect(service_response.payload[:vulnerability_ids]).to be_nil
        expect(service_response.payload[:error]).not_to be_nil
        expect(service_response.payload[:error]).to be_a_kind_of(ArgumentError)
        expect(service_response.payload[:error].message).to eq(
          'Pipeline must have a corresponding user to use as vulnerability author'
        )
      end
    end
  end
end
